#include "LogImpl.h"
#include <sstream>
#include <iostream>
#include "recovery/fs/file/FileLogOperation.h"

#include "Topic.h"

#include <iostream>

using recovery::Status;
using recovery::fs::file::FileLogOperation;

LogImpl::Meta::Meta(LogImpl & logImpl) 
: _logImpl(logImpl)
, _logSize(new LogImplSize(logImpl))
{
}

LogImpl::Meta::~Meta() {
    delete _logSize;
}
size_t LogImpl::Meta::offset() const {
    size_t size = 0;
    _logImpl.size(size);
    return size;
}
LogImpl::Meta & LogImpl::Meta::setOffset(size_t offset){
    _logImpl.saveLogHeader(offset);
    _logImpl.writeLogHeader(_logImpl._header); //TODO: interface for return code
    return *this;
}
LogImpl::Meta & LogImpl::Meta::addOffset(size_t offset){
    _logImpl._header.incBodySize(offset);
    _logImpl.writeLogHeader(_logImpl._header); //TODO: interface for return code
    return *this;
}
recovery::Status LogImpl::Meta::sync(){
    _logImpl.sync();
    return Status::OK();
}
std::string LogImpl::LogHeader::toString() const {
    std::ostringstream oss;
    oss << "sizeof(LogHeader):" << size() << " LogBodySize:" << bodySize() << std::endl;
    return oss.str();
}

LogImpl::LogImpl(Topic & topic)
: _topic(topic)
, _meta(*this)
, _header()
{
}
LogImpl::~LogImpl(){
}

Status LogImpl::open(){
	if(_topic.open() == 0){ 
        //Note: !!!!!!Restart logic!!!!!
        
        //1.no-exist then create it  or open exist log it 
        auto s = FileLogOperation::exist(_topic.logPath()) ? 
            FileLogOperation::openExist(_topic.logPath(), _log) : 
            FileLogOperation::create(_topic.logPath(), _log);
        if(!s) return s.toStatus();

        //2. get size of log file
        size_t logFileSize = 0;
        s = _log->size(logFileSize);
        if(!s) return s.toStatus();

        //3. compare file size to Meta.size(); sizeof(LogHeader)
        if(logFileSize >= LogHeader::HeaderSize()){
            //3.1 load meta  if valid 
            auto s = readLogHeader(_header); //positionedRead
            if(!_header.isValid(logFileSize)){
                std::cerr << "Log File Header invalid, Header:" << _header.toString()  << std::endl;
                return Status::Failed("Log Header Invalid"); //TODO: 
            }
        }
        else{  // file size < sizeof(LogHeader)
            if(logFileSize > 0){
                s = _log->truncate(0);
                if(!s) return s.toStatus();
            }
            _header.reset();

            auto s = writeLogHeader(_header); //positionedWrite
            if(!s) return s.toStatus();
        }

        //seek to End for file pointer
        return _log->seekEnd();
	}
    return Status::Failed("open Topic failed!");
}
Status LogImpl::close(){
	if(_log->close()){
	    return _topic.close() == 0 ? Status::OK() : Status::Failed("topic close failed!");
    }
    return Status::Failed("log file close failed!");
}


/*  
class ITransHeader {
public:
    virtual ~TransHeader(){}
    virtual size_t size() const = 0;
    virtual const uint8_t * data() const = 0;

    virtual void setId(size_t Id) = 0;
    virtual void setBodySize(const size_t bodySize) = 0;
};*/
//TODO: see the transaction-processing-principle book Chapter-9
//TODO: extrace TransHeader Interface
//TODO: id for automatic load & save 
class TransHeader {
public:
	TransHeader(size_t bodySize) : _bodySize(bodySize) {}
	~TransHeader(){}
	static constexpr size_t HeaderSize() {return sizeof(size_t) + sizeof(size_t);}

	size_t size() const { return TransHeader::HeaderSize(); }
	const uint8_t * data() const { return reinterpret_cast<const uint8_t*>(&_Id);}
	void setId(size_t Id) { this->_Id = Id;}
	void setBodySize(const size_t bodySize) { this->_bodySize = bodySize; }
private:
	size_t _Id = 0;
	size_t _bodySize = 0;
};

Status LogImpl::append(const std::string & data, size_t & appendedBytes) {
    TransHeader transHeader(data.size());
    //write trans_header to log
    if(_log->write(transHeader.data(), transHeader.size(), appendedBytes)){
        size_t bytes = 0;
        auto s = _log->write(data, bytes);
        if(s){
            appendedBytes += bytes;
        }
        return s.toStatus();
    }
    return Status::Failed("write transaction header error!");
}
Status LogImpl::sync() {
    return _log->sync().toStatus();
}
Status LogImpl::truncate(size_t size) { 
    return _log->truncate(size).toStatus();
}
Status LogImpl::size(size_t & sz) const { 
    sz = _header.bodySize() + LogHeader::HeaderSize(); 
    return Status::OK(); 
}

recovery::Status LogImpl::readLogHeader(LogHeader & header) const {
    size_t readBytes = 0;
    uint8_t buf[512] = {0}; 
    auto s =_log->positionedRead(0, buf, sizeof(buf), readBytes);
    if(s){
        header.setBodySize(*(reinterpret_cast<size_t*>(buf)));
    }
    return s.toStatus();
}
recovery::Status LogImpl::writeLogHeader(const LogHeader & header) const {
    size_t writedBytes = 0;
    return _log->positionedWrite(0, header.data(), header.size(), writedBytes);
}
